#pragma once

#include <MyControls/Segment.h>
#include <MyControls/SegmentCommands.h>
#include <MyControls/Property.h>
#include <MyControls/CompositeProperty.h>
#include <MyControls/PropertyGroup.h>
#include <MyControls/StdProperties.h>
#include <MyControls/MaskEditor.h>

#include "trm/segment.h"
#include "Types.h"
#include "MaskMode.h"
#include "MasksCollection.h"

typedef trm::bit_mask BitMask;
typedef trm::grey_mask GreyMask;
typedef trm::bezier_contour BezierContour;
typedef trm::twomasks_const_sptr TwoMasksConstSPtr;

class CXComp;
class CXSegmentSet;
class CXSegment;

class SegmentData : public trm::segment
{
public:
	CXSegment * m_pOwner;
public:
	SegmentData(int org = -1, FootageSPtr const & f = FootageSPtr(), CXSegment * Owner = 0);
public: //trm::segment interface
	virtual void frame_done(int frame, trm::direction dir, span const & calced_span);
	virtual bool is_canceled() const;
};

typedef boost::shared_ptr<SegmentData> SegmentDataSPtr;

typedef trm::param CXParams;

template <>
class My::Property<CXParams> : public My::StructProperty<CXParams> {
public:
	Property();
public:
	virtual void Init();
};

class CXSegment : 
	public My::SegmentCommands,
	public My::Segment,
	public My::MaskOwner,
	public My::Editable<CXSegment>
{
	friend class SegmentParamsAction;
	friend class SegmentMaskAction;
	DECLARE_MESSAGE_MAP()
public:
	CXSegment(CXSegmentSet &, int org = -1);
	CXSegment(CXSegmentSet &, CXSegment const & Sample, int org);
	~CXSegment();
public:
	CXSegmentSet & GetOwner();
	const CXComp & GetComp() const;
	CXComp & GetComp();
	xml::stream & Serialize(xml::stream & s);
	bool Valid() const;
public:
	virtual int GetOrigin() const;
	virtual Span GetTimeSpan() const;
	virtual void SetTimeSpan(Span);
	virtual MaskMode GetMaskMode() const;
	virtual void SetMaskMode(MaskMode);
	virtual Span GetDataSpan() const;
	virtual bool IsProcessed() const;
	virtual bool NeedsProcessing() const;
	virtual bool StartProcessing();
	virtual void CancelProcessing(bool Wait);
	virtual bool IsMaskEditing() const;
	virtual void OpenMaskEditor();
	virtual void CloseMaskEditor();
	virtual void Release();
public:
	// SegmentCommands
	virtual My::Segment * GetSegment()
		{ return this; }
	virtual My::Sequence * GetSequence();
private:
	void DoSetTimeSpan(Span);
	void DoSetMaskMode(MaskMode);
public:
	BitMask const & GetKeyMask() const;
	void SetKeyMask(BitMask const & m, const String & action = String());
	bool HasKeyMask() const;
	const CXParams & GetParams() const;
	void SetParams(const CXParams &);
	/// Returns intersection of TimeSpan and DataSpan
	Span GetActiveDataSpan() const { return m_TimeSpan.Intersection(m_DataSpan); }
	/// Returns true if there are data to be calculated
	bool IsUndercalculated() const { return m_TimeSpan.first < m_DataSpan.first || m_TimeSpan.last > m_DataSpan.last; }
	/// Returns true if trim operation will change segment's data
	bool IsTrimmable() const { return m_TimeSpan.first > m_DataSpan.first || m_TimeSpan.last < m_DataSpan.last; }
	/// Returns true if there are mask and data to be calculated and the segment is not enqueued now
	bool CanStartProcessing() const { return HasKeyMask() && IsUndercalculated() && !IsProcessed(); }
	void DoTrimDataSpan();
public: // MaskOwner
	virtual const BitMask & GetMask() const
		{ return GetKeyMask(); }
	virtual void SetMask(const BitMask & m, const String & action = String())
		{ SetKeyMask(m, action); }
public: //Frame is measured from composition's begin
	bool HasBitMask(Size Frame) const { return m_pData->has_bit_mask(int(Frame)); } 
	BitMask const & GetBitMask(Size Frame) const { return m_pData->bit_mask(int(Frame)); } 
	bool HasGreyMask(Size Frame) const { return m_pData->has_grey_mask(int(Frame)); } 
	void GetGreyMask(Size Frame, GreyMask & Mask) const { return m_pData->grey_mask(int(Frame), Mask); } 
	bool HasContour(Size Frame) const { return m_pData->has_contour(int(Frame)); } 
	BezierContour const & GetContour(Size Frame) const { return m_pData->contour(int(Frame)); } 
	TwoMasksConstSPtr const GetBothMasks(Size Frame) const { return m_pData->frame_masks(int(Frame)); } 
	MaskWithCombMode GetMaskWithCombMode(Size Frame) const { return MaskWithCombMode(GetBothMasks(Frame), m_MaskMode); } 
public:
	void OnProcessingFinish(); ///< called from Processor or ProcessQueue
	void OnProcessingProgress(Span DataSpan, int LastProcessedFrame);
	SegmentData & Data() { return *m_pData; }
	SegmentDataSPtr const & GetData() { return m_pData; }
	void SetData(SegmentDataSPtr const & d);
public:
	afx_msg void OnImportKeyMask();
	afx_msg void OnUpdateImportKeyMask(CCmdUI *pCmdUI);
	afx_msg void OnExportKeyMask();
	afx_msg void OnUpdateExportKeyMask(CCmdUI *pCmdUI);
	afx_msg void OnCopyContour();
	afx_msg void OnExportContour();
	afx_msg void OnUpdateExportContour(CCmdUI *pCmdUI);
	afx_msg void OnTrim();
	afx_msg void OnUpdateTrim(CCmdUI *pCmdUI);
	afx_msg void OnResetParameters();
	afx_msg void OnUpdateResetParameters(CCmdUI *pCmdUI);
public:
	class ParamsChange : public Event {
	public:
		ParamsChange(Segment & s) : Event(s) { }
	};
	class MaskModeChange : public Event {
	public:
		MaskModeChange(Segment & s) : Event(s) { }
	};
private:
	void Init(int org);
	void DoSetKeyMask(BitMask const & m);
	bool DoSetParams(const CXParams &); ///< return false if new parameters are the same as old one and nothing was changed
	void SetDataSpan(Span DataSpan);
	void WriteContour(std::ostream & out);
private:
	CXSegmentSet &	m_Owner;
	int				m_Origin;
	Span			m_TimeSpan;
	Span			m_DataSpan; ///< data span is less or equal to m_pData->data_span(),
								///< and less than m_pData->data_span() during processing
	SegmentDataSPtr m_pData;
	bool			m_Enqueued; ///< set in StartProcessing(), reset in Processor
	MaskMode		m_MaskMode;
public:
	static My::Property<Span>		TimeSpan;
	static My::Property<MaskMode>	MaskModeProp;
	static My::Property<CXParams>	Params;
	virtual const char * GetEditCaption() const
		{ return "Segment"; }
	static void InitPropertyMap(My::PropertyMap &);
};

inline xml::stream & operator || (xml::stream & s, CXSegment & g)
	{ return g.Serialize(s); }

